Skip to content

perf: Cache HTTP requests across all pages#174

Merged
azu merged 2 commits intotextlint-rule:masterfrom
ajfAfg:perf/cache-http-requests-across-all-pages
Feb 2, 2026
Merged

perf: Cache HTTP requests across all pages#174
azu merged 2 commits intotextlint-rule:masterfrom
ajfAfg:perf/cache-http-requests-across-all-pages

Conversation

@ajfAfg
Copy link
Contributor

@ajfAfg ajfAfg commented Jan 21, 2026

Background

This package caches HTTP requests used to verify link existence. However, since the cache was created per page, duplicate links across pages were causing cache misses.

Approach

I changed the caching to be global, not per page.
I also modified the cache key to include the rule's options. While actual use assumes options don't vary per page, test cases may have different options during testing.

Additionally, the rule with these changes occasionally hung, so I addressed this by always consuming Response.body.

Comment on lines 187 to 276
@@ -214,6 +218,7 @@ const createCheckAliveURL = (ruleOptions: Options, resolvePath: (path: string, b
const finalRes = await fetchWithDefaults(redirectedUrl, { ...opts, redirect: "follow" });
const url = URL.parse(uri);
const hash = url?.hash || null;
await finalRes.body?.cancel();
return {
ok: finalRes.ok,
redirected: true,
@@ -264,6 +269,10 @@ const createCheckAliveURL = (ruleOptions: Options, resolvePath: (path: string, b
ok: false,
message: ex.message
};
} finally {
if (res && !res.body?.locked) {
await res.body?.cancel();
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it okay to understand that this change has nothing to do with Cache?

https://github.com/nodejs/undici?tab=readme-ov-file#garbage-collection
I feel like it's good to explicitly consume because you can't rely on GC with Node fetch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, in my environment, after placing the cache globally, it started hanging occasionally. Therefore, I believe it is appropriate to include this change in this PR.

const memorizedIsAliveURIByOptions = new Map<
string,
ReturnType<typeof pMemoize<ReturnType<typeof createCheckAliveURL>>>
>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have a Cache Map globally, you will behave like a memory leak if you don't condition it by size or TTL like LRU Cache.
CLI is not a big problem, but if you run it in a server-like shape or editor, it will always refer to the same global cache, so I think memory consumption will increase linearly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function pMemoize allows setting the cache TTL. Since the rule textlint-rule-no-dead-link sets it to 30 seconds by default, I believe there is no need to implement TTL again. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 memorizedIsAliveURIByOptions is still global variables.

@ajfAfg ajfAfg requested a review from azu January 22, 2026 09:07
@ajfAfg
Copy link
Contributor Author

ajfAfg commented Jan 31, 2026

@azu
We would be grateful if you could review this pull request.

const memorizedIsAliveURIByOptions = new Map<
string,
ReturnType<typeof pMemoize<ReturnType<typeof createCheckAliveURL>>>
>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 memorizedIsAliveURIByOptions is still global variables.

@azu azu added the Type: Refactoring A code change that neither fixes a bug nor adds a feature label Feb 2, 2026
@azu azu merged commit a60b2c4 into textlint-rule:master Feb 2, 2026
6 checks passed
@github-actions github-actions bot mentioned this pull request Feb 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Refactoring A code change that neither fixes a bug nor adds a feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants